﻿using Application.Adapter;
using Application.App.Command;
using Application.Common.Extensions;
using Domain;
using Domain.Enums;
using Domain.Models;
using Domain.Models.Cmd;
using Domain.Options;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Sockets;

namespace Application.Plugins
{
    public class ReceivePlugin : PluginBase, ITcpReceivedPlugin<ISocketClient>, ITcpConnectedPlugin<ISocketClient>
    {
        //private readonly int m_timeTick;
        private readonly ILog logger;
        private MediatR.IMediator _mediator;
        private readonly ApiConfigOptions apiConfigOptions;
        private readonly IHttpClientFactory _httpClientFactory;
        //如何客户端使用插件，则core ioc中不能注入
        private readonly IServiceProvider _serviceProvider;
        private readonly ITcpService _tcpService;

        public ReceivePlugin(ILog logger, IServiceProvider serviceProvider, IOptions<ApiConfigOptions> options, IHttpClientFactory httpClientFactory, IMediator mediator)
        {
            //多个客户端时  无法注入
            //if (timeTick == null)
            //{
            //    this.m_timeTick = 6000;
            //}
            //else
            //{
            //    this.m_timeTick = timeTick.Value.timeTick;
            //}
            this.logger = logger;
            this.apiConfigOptions = options.Value;
            _httpClientFactory = httpClientFactory;
            _mediator = mediator;
            _serviceProvider = serviceProvider;
            _mediator = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService<IMediator>();
        }


        /// <summary>
        /// 上 代表接收到客户端数据
        /// </summary>
        /// <param name="client"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        /// <exception cref="CloseException"></exception>
        async Task ITcpReceivedPlugin<ISocketClient>.OnTcpReceived(ISocketClient client, ReceivedDataEventArgs e)
        {
            Console.WriteLine(nameof(ReceivePlugin));
            //从客户端收到信息
            if (e.ByteBlock != null)
            {
                //直接传输string，会被IBetweenAndRequestInfo 的所拦截，根本不会接受到数据
                var mes = Encoding.UTF8.GetString(e.ByteBlock.Buffer, 0, e.ByteBlock.Len);
                if (mes == "close")
                {
                    throw new CloseException(mes);
                }
                client.Logger.Info($"已从{client.GetIPPort()}接收到信息：{mes}");

                client.Send(mes);//将收到的信息直接返回给发送方
            }
            else
            {
                if (e.RequestInfo is BetweenAndRequestInfo myRequest)
                {
                    //验证校验码
                    if (!myRequest.VaildSuccess)
                    {
                        logger.Warning($"body is Invalid : {Convert.ToHexString(myRequest.Body)}");
                        return;
                    }
                    var socketId = string.Empty;
                    var deviceIdStr = Convert.ToHexString(myRequest.DeviceId);
                    socketId = Consts.DeviceToSocketId[deviceIdStr];
                    ISocketClient socketClient;
                    List<byte> buffer = new();
                    byte packageLength;


                    string apiData = string.Empty;
                    switch (myRequest.DataType)
                    {
                        case DataType.校准实际出酒命令下:
                        case DataType.校准出酒命令下:
                        case DataType.通道校准时实际出酒命令下:
                        case DataType.通道校准出酒下:
                            await Console.Out.WriteLineAsync("收到api校准命令1：" + Convert.ToHexString(myRequest.Body));
                            apiData = ("A5" + Convert.ToHexString(myRequest.Body) + "5A");
                            if (client.Service.SocketClients.TryGetSocketClient(socketId, out socketClient))
                            {
                                try
                                {
                                    socketClient.Send(apiData.HexStringToByteArray());
                                    Console.WriteLine($"校准成功：{apiData}");
                                }
                                catch (Exception)
                                {
                                    Console.WriteLine("校准失败，发生异常");
                                }
                            }
                            else
                            {
                                Console.WriteLine($"校准失败，获取复用socketid失败,当前使用socketId:{socketId}");
                            }
                            break;
                        case DataType.测试两个tcp:
                        case DataType.设备开锁命令下:
                        case DataType.广告更新命令下:
                        case DataType.定时开关机下:
                        case DataType.远程升级固件下:
                        case DataType.更改设备ID号下:
                        case DataType.应答取消出酒命令下:
                        case DataType.修改服务器IP下:
                        case DataType.查询设备命令下:
                        case DataType.出酒命令下:
                        case DataType.通道刷卡应答下:
                        case DataType.位置信息上报应答下:
                        case DataType.平台应答出酒完成上报下:
                            //1, 接受api数据， 暂存api中socket
                            await Console.Out.WriteLineAsync("收到api命令1：" + Convert.ToHexString(myRequest.Body));

                            apiData = ("A5" + Convert.ToHexString(myRequest.Body) + "5A");
                            //2，获取device的socket  发送并接受device的数据
                            var getsc = client.Service.SocketClients.TryGetSocketClient(socketId, out socketClient);
                            if (!getsc)
                                break;
                            Console.WriteLine($"{socketId}__________111111");


                            //var result = socketClient.GetWaitingClient(new WaitingOptions() { AdapterFilter = AdapterFilter.NoneAll, BreakTrigger = true, ThrowBreakException = true }).SendThenResponse(apiData.HexStringToByteArray(), timeout: 20000);
                            //await Console.Out.WriteLineAsync($"服务器 转发到设备 返回数据：{Convert.ToHexString(result.Data)}");

                            try
                            {
                                socketClient.DefaultSend(apiData.HexStringToByteArray());
                            }
                            catch (Exception ee)
                            {
                                Console.WriteLine($"设备{deviceIdStr}.{myRequest.DataType}下行命令失败 : err{ee.Message},{ee.StackTrace}");
                            }

                            //保存设备查询命令socket
                            if (myRequest.DataType == DataType.查询设备命令下)
                            {
                                Consts.DeviceToApiSocket.TryAdd(deviceIdStr, new());
                                Consts.DeviceToApiSocket[deviceIdStr].Add(client);
                            }
                            break;
                        case DataType.设备应答出酒命令上:
                            Consts.devices[deviceIdStr].OOWState = 1;
                            var devRepOOwUpCall = new DeviceStateCall() { device = deviceIdStr, state = 3 };
                            Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} PostAsync:" + JsonConvert.SerializeObject(devRepOOwUpCall));
                            using (var httpClient = _httpClientFactory.CreateClient("BaseCallBackUrl"))
                            {
                                var res = await httpClient.PostAsync(apiConfigOptions.StateUpdateUrl, new StringContent(JsonConvert.SerializeObject(devRepOOwUpCall), Encoding.UTF8, "application/json"));
                            }
                            break;
                        case DataType.设备出酒完成上报上:
                            Consts.devices[deviceIdStr].OOWState = 0;

                            //自动应答
                            var data = (DeviceOOWEndUp)myRequest.Data;
                            Consts.CommandPackageLength.TryGetValue(DataType.平台应答出酒完成上报下, out packageLength);
                            buffer = new List<byte>() { Consts.StartByte, packageLength };
                            buffer.AddRange(deviceIdStr.HexStringToByteArray());
                            buffer.Add(((int)DataType.平台应答出酒完成上报下).ToByte());
                            buffer.Add(data.ChannelNumber.ToByte());
                            buffer.Add(1.ToByte());
                            buffer.Add(buffer.GetVaildByteCode());
                            buffer.Add(Consts.EndByte);
                            client.DefaultSend(buffer.ToArray());


                            try
                            {
                                var GetData = (DeviceOOWEndUp)myRequest.Data;
                                var callData = new DeviceStateCall
                                {
                                    device = deviceIdStr,
                                    state = 1,
                                    oow = GetData.WineNumber
                                };
                                Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} PostAsync:" + JsonConvert.SerializeObject(callData));
                                using var httpClient = _httpClientFactory.CreateClient("BaseCallBackUrl");
                                var res = await httpClient.PostAsync(apiConfigOptions.StateUpdateUrl, new StringContent(JsonConvert.SerializeObject(callData), Encoding.UTF8, "application/json"));
                            }
                            catch (Exception)
                            {
                                await Console.Out.WriteLineAsync($"{deviceIdStr}设备出酒完成回调失败");
                            }

                            await Console.Out.WriteLineAsync("服务器 -> 设备 设备出酒完成上报下 " + Convert.ToHexString(buffer.ToArray()));
                            break;
                        case DataType.设备暂停或继续出酒上报上:
                            Consts.devices[deviceIdStr].OOWState = 2;
                            var StopOrContinueState = myRequest.Body[7];
                            //暂停出酒回调
                            if (StopOrContinueState == 2)
                            {
                                var callData2 = new
                                {
                                    device = deviceIdStr,
                                    state = 2
                                };
                                try
                                {
                                    Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} PostAsync:" + JsonConvert.SerializeObject(callData2));
                                    using var httpClient = _httpClientFactory.CreateClient("BaseCallBackUrl");
                                    await httpClient.PostAsync(apiConfigOptions.StateUpdateUrl, new StringContent(JsonConvert.SerializeObject(callData2), Encoding.UTF8, "application/json"));
                                }
                                catch (Exception)
                                {
                                    Console.WriteLine("暂停出酒回调失败");
                                }
                            }
                            break;
                        case DataType.设备应答查询上:
                            Consts.devices[deviceIdStr].State = 1;

                            var dId = myRequest.deviceId;
                            apiData = ("A5" + Convert.ToHexString(myRequest.Body) + "5A");
                            _ = Task.Factory.StartNew(() =>
                            {
                                if (!Consts.DeviceToApiSocket.ContainsKey(dId))
                                {
                                    return;
                                }
                                var list = Consts.DeviceToApiSocket[dId].ToList();
                                if (list == null || !list.Any())
                                {
                                    return;
                                }
                                Console.WriteLine($"应答通过api查询设备{dId}，回复的api接口socket集合为:{JsonConvert.SerializeObject(list.Select(l => new { id = l.Id, ip = l.IP, port = l.Port }))}");
                                Consts.DeviceToApiSocket[dId].Clear();
                                foreach (var apiSocket in list)
                                {
                                    if (!apiSocket.Online)
                                    {
                                        continue;
                                    }
                                    apiSocket.DefaultSend(apiData.HexStringToByteArray());
                                }
                            });
                            break;
                        case DataType.设备开锁应答上:
                            break;
                        case DataType.修改IP应答上:
                            break;
                        case DataType.设备到服务器心跳包上:
                            Console.WriteLine($"设备上报心跳1 time:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}  body:{Convert.ToHexString(myRequest.Body)}");
                            client.CustomPong(myRequest);
                            break;
                        case DataType.心跳包应答下:
                            break;
                        case DataType.校准出酒应答上:
                            break;
                        case DataType.校准实际出酒应答上:
                            break;
                        case DataType.设备位置信息上报上:
                            //自动应答  测试是不是断开socket的原因
                            Consts.CommandPackageLength.TryGetValue(DataType.位置信息上报应答下, out packageLength);
                            buffer = new List<byte>() { Consts.StartByte, packageLength };
                            buffer.AddRange(deviceIdStr.HexStringToByteArray());
                            buffer.Add(((int)DataType.位置信息上报应答下).ToByte());
                            buffer.Add(buffer.GetVaildByteCode());
                            buffer.Add(Consts.EndByte);
                            client.DefaultSend(buffer.ToArray());
                            await Console.Out.WriteLineAsync("自动应答：" + Convert.ToHexString(buffer.ToArray()));
                            await Console.Out.WriteLineAsync("服务器 -> 设备 位置下 " + Convert.ToHexString(buffer.ToArray()));
                            break;
                        case DataType.取消出酒命令上:



                            break;
                        case DataType.应答更改设备ID号上:
                            break;

                        case DataType.远程升级固件应答上:

                            Consts.CommandPackageLength.TryGetValue(DataType.远程升级固件下, out packageLength);
                            buffer = new List<byte>() { Consts.StartByte, packageLength };
                            buffer.AddRange(deviceIdStr.HexStringToByteArray());
                            buffer.Add(((int)DataType.远程升级固件下).ToByte());
                            buffer.Add(buffer.GetVaildByteCode());
                            buffer.Add(Consts.EndByte);
                            client.DefaultSend(buffer.ToArray());
                            await Console.Out.WriteLineAsync("自动应答：" + Convert.ToHexString(buffer.ToArray()));
                            break;
                        case DataType.定时开关机应答上:
                            break;
                        case DataType.通道校准出酒应答上:
                            break;
                        case DataType.通道校准时实际出酒应答上:
                            break;
                        case DataType.通道刷卡上报命令:
                            break;
                        case DataType.广告更新应答上:
                            break;
                        default:
                            break;
                    }
                    e.Handled = true;
                }
            }
            //await Task.Delay(1000);
        }

        public async Task OnTcpConnected(ISocketClient client, ConnectedEventArgs e)
        {
            await e.InvokeNext();
        }
    } /// <summary>
      /// 应一个网友要求，该插件主要实现，在接收数据时如果触发<see cref="CloseException"/>异常，则断开连接。
      /// </summary>
    class ClosePlugin : PluginBase, ITcpReceivedPlugin<ISocketClient>
    {
        private readonly ILog m_logger;

        public ClosePlugin(ILog logger)
        {
            this.m_logger = logger;
        }

        async Task ITcpReceivedPlugin<ISocketClient>.OnTcpReceived(ISocketClient client, ReceivedDataEventArgs e)
        {
            try
            {
                await e.InvokeNext();
            }
            catch (CloseException ex)
            {
                m_logger.Info("拦截到CloseException");
                client.Close(ex.Message);
            }
            catch (Exception exx)
            {

            }
            finally
            {

            }
        }
    }
    class CloseException : Exception
    {
        public CloseException(string msg) : base(msg) { }
    }
}
